//
//  CCMatrix3.cpp
//  ShadowMapping
//
//  Created by Daniel Corleone on 16/6/14.
//  Copyright (c) 2014年 iCloverStudio. All rights reserved.
//

#ifndef __CC_MATRIX3_CPP__
#define __CC_MATRIX3_CPP__

#include "CCMatrixs.h"

template<typename T>
Matrix3<T>::Matrix3()
: m00(0), m01(0), m02(0)
, m10(0), m11(0), m12(0)
, m20(0), m21(0), m22(0)
{}

template<typename T>
Matrix3<T>::Matrix3(const T src[9])
: m00(src[0]), m01(src[1]), m02(src[2])
, m10(src[3]), m11(src[4]), m12(src[5])
, m20(src[6]), m21(src[7]), m22(src[8])
{}

template<typename T>
Matrix3<T>::Matrix3(value_type xx, value_type xy, value_type xz,
                    value_type yx, value_type yy, value_type yz,
                    value_type zx, value_type zy, value_type zz)
: m00(xx), m01(xy), m02(xz)
, m10(yx), m11(yy), m12(yz)
, m20(zx), m21(zy), m22(zz)
{}

template<typename T>
Matrix3<T>::Matrix3(const Matrix3<T>& other)
: m00(other[0]), m01(other[1]), m02(other[2])
, m10(other[3]), m11(other[4]), m12(other[5])
, m20(other[6]), m21(other[7]), m22(other[8])
{}

template<typename T>
void
Matrix3<T>::set(const T src[9])
{
    m00 = src[0]; m01 = src[1]; m02 = src[2];
    m10 = src[3]; m11 = src[4]; m12 = src[5];
    m20 = src[6]; m21 = src[7]; m22 = src[8];
    return;
}

template<typename T>
void
Matrix3<T>::set(value_type xx, value_type xy, value_type xz,
                value_type yx, value_type yy, value_type yz,
                value_type zx, value_type zy, value_type zz)
{
    m00 = xx; m01 = xy; m02 = xz;
    m10 = yx; m11 = yy; m12 = yz;
    m20 = zx; m21 = zy; m22 = zz;
    return;
}

template<typename T>
void
Matrix3<T>::setRow(Int32 index, const T row[3])
{
    Int32 _index = index * 3;
    m[_index] = row[0];
    m[_index + 1] = row[1];
    m[_index + 2] = row[2];
    return;
}

template<typename T>
void
Matrix3<T>::setRow(Int32 index, const Vector3<T>& v) {
    Int32 _index = index * 3;
    m[_index] = v.x;
    m[_index + 1] = v.y;
    m[_index + 2] = v.z;
    return;
}

template<typename T>
void
Matrix3<T>::setColumn(Int32 index, const T col[3]) {
    m[index] = col[0];
    m[index + 3] = col[1];
    m[index + 6] = col[2];
    return;
}

template<typename T>
void
Matrix3<T>::setColumn(Int32 index, const Vector3<T>& v) {
    m[index] = v[0];
    m[index + 3] = v[1];
    m[index + 6] = v[2];
    return;
}

template<typename T>
const T*
Matrix3<T>::get() const {
    return m;
}

template<typename T>
Real
Matrix3<T>::getDeterminant()
{
    return (m[0] * (m[4] * m[8] - m[5] * m[7]) -
            m[1] * (m[3] * m[8] - m[5] * m[6]) +
            m[2] * (m[3] * m[7] - m[4] * m[6]));
}

template<typename T>
Matrix3<T>&
Matrix3<T>::identity()
{
    m[0] = m[4] = m[8] = 1.0;
    m[1] = m[2] = m[3] = m[5] = m[6] = m[7] = 0.0;
    return *this;
}

template<typename T>
Matrix3<T>&
Matrix3<T>::transpose() 
{
    std::swap(m[1], m[3]);
    std::swap(m[2], m[6]);
    std::swap(m[5], m[7]);
    return *this;
}

template<typename T>
Matrix3<T>&
Matrix3<T>::invert()
{
    Real determinant, invDeterminant;
    T tmp[9];
    
    tmp[0] = m[4] * m[8] - m[5] * m[7];
    tmp[1] = m[2] * m[7] - m[1] * m[8];
    tmp[2] = m[1] * m[5] - m[2] * m[4];
    tmp[3] = m[5] * m[6] - m[3] * m[8];
    tmp[4] = m[0] * m[8] - m[2] * m[6];
    tmp[5] = m[2] * m[3] - m[0] * m[5];
    tmp[6] = m[3] * m[7] - m[4] * m[6];
    tmp[7] = m[1] * m[6] - m[0] * m[7];
    tmp[8] = m[0] * m[4] - m[1] * m[3];
    
    // check determinant if it is 0
    determinant = m[0] * tmp[0] + m[1] * tmp[3] + m[2] * tmp[6];
    if(::fabs(determinant) <= FLOAT_MIN) {
        return identity(); // cannot inverse, make it idenety matrix
    }
    
    // divide by the determinant
    invDeterminant = 1.0f / determinant;
    m[0] = invDeterminant * tmp[0];
    m[1] = invDeterminant * tmp[1];
    m[2] = invDeterminant * tmp[2];
    m[3] = invDeterminant * tmp[3];
    m[4] = invDeterminant * tmp[4];
    m[5] = invDeterminant * tmp[5];
    m[6] = invDeterminant * tmp[6];
    m[7] = invDeterminant * tmp[7];
    m[8] = invDeterminant * tmp[8];
    
    return *this;
}

template<typename T>
Matrix3<T>
Matrix3<T>::operator+(const Matrix3<T>& other) const {
    return Matrix3<T>(m[0] + other[0], m[1] + other[1], m[2] + other[2],
                      m[3] + other[3], m[4] + other[4], m[5] + other[5],
                      m[6] + other[6], m[7] + other[7], m[8] + other[8]);
}

template<typename T>
Matrix3<T>&
Matrix3<T>::operator+=(const Matrix3<T>& other) {
    m[0] += other[0];    m[1] += other[1];    m[2] += other[2];
    m[3] += other[3];    m[4] += other[4];    m[5] += other[5];
    m[6] += other[6];    m[7] += other[7];    m[8] += other[8];
    return *this;
}

template<typename T>
Matrix3<T>
Matrix3<T>::operator-(const Matrix3<T>& other) const {
    return Matrix3<T>(m[0] - other[0], m[1] - other[1], m[2] - other[2],
                      m[3] - other[3], m[4] - other[4], m[5] - other[5],
                      m[6] - other[6], m[7] - other[7], m[8] - other[8]);
}

template<typename T>
Matrix3<T>&
Matrix3<T>::operator-=(const Matrix3<T>& other) {
    m[0] -= other[0];    m[1] -= other[1];    m[2] -= other[2];
    m[3] -= other[3];    m[4] -= other[4];    m[5] -= other[5];
    m[6] -= other[6];    m[7] -= other[7];    m[8] -= other[8];
    return *this;
}

template<typename T>
Vector3<T>
Matrix3<T>::operator*(const Vector3<T>& v) const {
    return Vector3<T>(m[0] * v.x + m[1] * v.y + m[2] * v.z,
                      m[3] * v.x + m[4] * v.y + m[5] * v.z,
                      m[6] * v.x + m[7] * v.y + m[8] * v.z);
}

template<typename T>
Matrix3<T>
Matrix3<T>::operator*(const Matrix3<T>& v) const {
    return Matrix3<T>(m[0]*v[0] + m[1]*v[3] + m[2]*v[6],  m[0]*v[1] + m[1]*v[4] + m[2]*v[7],  m[0]*v[2] + m[1]*v[5] + m[2]*v[8],
                      m[3]*v[0] + m[4]*v[3] + m[5]*v[6],  m[3]*v[1] + m[4]*v[4] + m[5]*v[7],  m[3]*v[2] + m[4]*v[5] + m[5]*v[8],
                      m[6]*v[0] + m[7]*v[3] + m[8]*v[6],  m[6]*v[1] + m[7]*v[4] + m[8]*v[7],  m[6]*v[2] + m[7]*v[5] + m[8]*v[8]);
}

template<typename T>
Matrix3<T>&
Matrix3<T>::operator*=(const Matrix3<T>& other) {
    *this = *this * other;
    return *this;
}

template<typename T>
Boolean
Matrix3<T>::operator==(const Matrix3<T>& other) const {
    return (m[0] == other[0] && m[1] == other[1] && m[2] == other[2] &&
            m[3] == other[3] && m[4] == other[4] && m[5] == other[5] &&
            m[6] == other[6] && m[7] == other[7] && m[8] == other[8]);
}

template<typename T>
Boolean
Matrix3<T>::operator!=(const Matrix3<value_type> &other) const {
    return !(*this == other);
//    return (m[0] != other[0] || m[1] != other[1] || m[2] != other[2] ||
//            m[3] != other[3] || m[4] != other[4] || m[5] != other[5] ||
//            m[6] != other[6] || m[7] != other[7] || m[8] != other[8]);
}

template<typename T>
T
Matrix3<T>::operator[](Int32 index) const {
    return m[index];
}

template<typename T>
T&
Matrix3<T>::operator[](Int32 index) {
    return m[index];
}

template<typename T>
Matrix3<T>
Matrix3<T>::operator-() const {
    return Matrix3<T>(-m[0], -m[1], -m[2],
                      -m[3], -m[4], -m[5],
                      -m[6], -m[7], -m[8]);
}

template<typename T>
Matrix3<T>
Matrix3<T>::operator*(Real s) const {
    return Matrix3<T>(m[0]*s, m[1]*s, m[2]*s,
                      m[3]*s, m[4]*s, m[5]*s,
                      m[6]*s, m[7]*s, m[8]*s);
}

#endif      /// __CC_MATRIX3_CPP__
